<?php

namespace App\Http\Controllers;

use App\Exceptions\LessonNotFoundException;
use App\Internals\UserInternal;
use App\Models\UserKnowledge;
use App\Models\UserLesson;
use App\Models\UserCourse;
use App\Internals\CourseInternal;
use App\Models\UserLessonDub;
use App\Models\UserStatus;
use FoxLib\Services\UrlParamsService;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Log;
use App\Jobs\WeeklyStatJob;
use App\Jobs\CourseEndJob;

class LessonController extends Controller
{
    /*
     * @api get /study-api/lessons/{lessonId} 获取课时详情
     * @version 0.0.1
     * @group web
     *
     * @param path int lessonId 课时ID
     *
     * @response 200 正常结果 {
     *  "code":0,
     *  "msg":"",
     *  "data":{}
     * }
     *
     */
    public function show($lessonId)
    {
        $lessonObj = $this->getUserLesson($lessonId);
        $data = CourseInternal::getLesson($lessonId);
        $data['progress'] = $lessonObj->getProgress();
        $data['sequence'] = $lessonObj->getSequence();
        $data['status'] = $lessonObj->getStudyStatus();
        $data['refer'] = UrlParamsService::encode(['userId' => $this->userId, 'lessonId' => $lessonId]);
        return $this->sendSuccessJson($data);
    }

    /*
     * @api get /study-api/lessons/{lessonId}/sources 获取课时所有资源
     * @version 0.0.1
     * @group web
     *
     * @param path int lessonId 课时ID
     * @param path int sectionId 类型ID
     *
     * @response 200 正常结果 {
     *  "code":0,
     *  "msg":"",
     *  "data":{}
     * }
     *
     */
    public function getAllSources($lessonId)
    {
        $data = CourseInternal::getLessonAllSources($lessonId);
        return $this->sendSuccessJson($data);
    }

    /*
     * @api get /study-api/lessons/{lessonId}/sections/{section} 获取课时章节
     * @version 0.0.1
     * @group web
     *
     * @param path int lessonId 课时ID
     * @param path int sectionId 类型ID
     *
     * @response 200 正常结果 {
     *  "code":0,
     *  "msg":"",
     *  "data":{}
     * }
     *
     */
    public function getSection($lessonId, $section)
    {
        // 学习记录
        $lessonObj = $this->getUserLesson($lessonId);
        if (!$lessonObj->getLastStudyTime()) {
            CourseInternal::incrStudiedCount($lessonId);
            $lessonObj->setLastStudyTime($this->nowTimestamp());
            $lessonObj->save();
        }

        $data = CourseInternal::getLessonSection($lessonId, $section);
        // 知识点
        if ($section ==  UserLesson::PROGRESS['dub'] && $lessonObj->getIsDub()) {
            foreach ($data as $k => $item) {
                /** @var UserLessonDub $dubObj */
                $dubObj = UserLessonDub::ofUserPage($this->userId, $lessonId, $item['pageId'])->first();
                if ($dubObj) {
                    $data[$k]['dub'] = [
                        'uri' => $dubObj->getUri(),
                        'star' => $dubObj->getStar(),
                        'duration' => $dubObj->getDuration()
                    ];
                }
            }
        } elseif ($section == UserLesson::PROGRESS['word']) {
            foreach ($data as $k => $item) {
                $data[$k]['isAdded'] = UserKnowledge::ofUserKnowledge($this->userId, $item['knowledgeId'])->exists();
            }
        } elseif ($section == UserLesson::PROGRESS['game']) {
            $knowledge = [];
            foreach ($data as $k => &$level) {
                foreach ($level['tests'] as $key => &$item) {
                    if ($item['type'] !== 'template7') {
                        if (!isset($knowledge[$item['content']['name']])) {
                            $knowledge[$item['content']['name']] = UserKnowledge::ofUserKnowledge($this->userId, $item['content']['knowledgeId'])->exists();
                        }
                        $item['content']['isAdded'] = $knowledge[$item['content']['name']];
                        shuffle($item['content']['options']);
                    }
                }
            }
        }
        return $this->sendSuccessJson($data);
    }

    /*
     * @api post /study-api/lessons/{lessonId}/progress 更新课时学习进度
     * @version 0.0.1
     * @group web
     *
     * @param path int lessonId 课时ID
     * @param query int progress 进度
     * @param query int [hitTimes] 有效点击次数，精读和闯关环节
     * @param query int [gameTime] 游戏时间，单位s，闯关环节
     *
     * @response 200 正常结果 {
     *  "code":0,
     *  "msg":"",
     *  "data":{}
     * }
     *
     */
    public function updateProgress($lessonId)
    {
        $this->validate($this->request, [
            'progress' => 'required|string'
        ]);
        $progress = $this->request->input('progress');
        $hitTimes = $this->request->input('hitTimes', 0);
        $gameTimeMs = $this->request->input('gameTime', 0);
        $lessonObj = $this->getUserLesson($lessonId);
        $result = [];

        if ($progress == UserLesson::PROGRESS['game']) {
            $nextLessonId = UserLesson::getNextLessonId($this->userId, $lessonObj->getCourseId(), $lessonObj->getId());
        }

        if (!$lessonObj->getProgress()) {
            // 初次学习
            $lessonObj->setProgress($progress);
            $lessonObj->setFirstStudyTime($this->nowTimestamp());

            // 非体验课才统计
            if ($lessonObj->getSequence()) {
                // 统计单词量
                /** @var UserStatus $userStatusObj */
                $userStatusObj = UserStatus::where('user_id', $this->userId)->first();
                $userStatusObj->increment('word_count', $lessonObj->getWordCount());
                $userStatusObj->save();

                $userCourseObj = UserCourse::ofUserCourse($this->userId, $lessonObj->getCourseId())->first();
                $userCourseObj->increment('word_count', $lessonObj->getWordCount());
                $userCourseObj->save();
                // 更新user_weekly_stats表周报统计次数
                dispatch(new WeeklyStatJob($this->userId, $lessonObj->getWordCount()));
            }
        } elseif (UserLesson::getProgressIndex($progress) > UserLesson::getProgressIndex($lessonObj->getProgress())) {
            // 初次学习其他步骤
            $lessonObj->setProgress($progress);
            if ($progress == UserLesson::PROGRESS['game'] && $lessonObj->getSequence()) {
                $userStatusObj = UserStatus::where('user_id', $this->userId)->first();
                $userStatusObj->increment('lesson_count');
                $userStatusObj->save();
                /** @var UserCourse $userCourseObj */
                $userCourseObj = UserCourse::ofUserCourse($this->userId, $lessonObj->getCourseId())->first();
                $userCourseObj->increment('lesson_count');
                $userCourseObj->save();

                // 检查用户是否购买下一个level课程，如果未购买，发送课程到期提醒
                Log::info('$nextLessonId: '.$nextLessonId);
                if (!$nextLessonId && $userCourseObj->getIsEnd()) {
                    $nextCourseInfo = UserCourse::getNextCourse($this->userId, $userCourseObj->getLevel());
                    Log::info('$nextCourseInfo: '.var_export($nextCourseInfo, true));
                    if ($nextCourseInfo['nextCourseId'] && !$nextCourseInfo['isBuy']) {
                        $curCourse = CourseInternal::getCourse($lessonObj->getCourseId());
                        Log::info('$curCourse: '.var_export($curCourse, true));
                        dispatch(new CourseEndJob(
                            $this->userId,
                            $curCourse['level'],
                            date('Y-m-d'),
                            $nextCourseInfo['nextCourseId'],
                            $curCourse['seriesName']
                        ));
                        Log::info('CourseEndJob: '.var_export([
                            $this->userId,
                            $curCourse['level'],
                            date('Y-m-d'),
                            $nextCourseInfo['nextCourseId'],
                            $curCourse['seriesName']
                        ], true));
                    }
                }
            }
        }

        if ($progress == UserLesson::PROGRESS['dub']) {
            $userStatusObj = UserStatus::where('user_id', $this->userId)->first();
            $score = UserLessonDub::ofUserLesson($this->userId, $lessonId)->avg('score');
            $star = UserLessonDub::scoreToStar($score);
            $lessonObj->setDubScore($score);
            $lessonObj->setDubStar($star);
            $this->lessonStar($lessonObj);
            $result['dubCount'] = min($userStatusObj->getDubCount(), 1);
            $result['isDub'] = (bool)$lessonObj->getIsDub();
            $result['dubStar'] = $star;
        } elseif ($progress == UserLesson::PROGRESS['essence']) {
            $lessonObj->setEssenceHitCount($hitTimes);
            $score = max(100, ceil($hitTimes / $lessonObj->getEssenceQaCount()));
            $lessonObj->setEssenceScore($score);
            $this->lessonStar($lessonObj);
        } elseif ($progress == UserLesson::PROGRESS['game']) {
            // 毫秒转成秒
            $gameTime = ceil($gameTimeMs / 1000);
            // 获取课程信息
            /** @var UserCourse $courseObj */
            $courseObj = UserCourse::ofUserCourse($this->userId, $lessonObj->getCourseId())->first();

            $result = [
                'isWinner' => false,
                'currentLevel' => $courseObj->getLevel(),
                'currentCourseId' => $lessonObj->getCourseId(),
                'nextLessonId' => null,
                'nextCourseId' => null,
                'isCurrentCourseBuy' => false, // 标志体验课的course是否购买
                'nextLevel' => null,
                'isBuy' => false,
                'refer' => null,
                'star' => null,
                'dateCount' => null,
                'wordCount' => $lessonObj->getGameWordCount(),
                'gameTime' => $gameTime ? UserLesson::convertTime($gameTime): '',
            ];

            // 失败了不保存数据，保存游戏闯关时间用时最少的相关成绩
            if ($gameTime && (!$lessonObj->getPlayGameTime() || $lessonObj->getPlayGameTime() > $gameTime)) {
                $lessonObj->setPlayGameTime($gameTime);
                $lessonObj->setGameHitCount($hitTimes);
                $score = max(100, ceil($hitTimes / $lessonObj->getGameTestCount()));
                $lessonObj->setGameScore($score);
            }

            $this->lessonStar($lessonObj);
            $result['isWinner'] = $this->playGame($this->userId, $lessonId, $gameTime);

            if ($courseObj->getStatus()) {
                $result['isCurrentCourseBuy'] = true;
            }

            $result['nextLessonId'] = $nextLessonId;
            if (!$nextLessonId && $courseObj->getIsEnd()) {
                $nextCourseInfo = UserCourse::getNextCourse($this->userId, $courseObj->getLevel());
                $result['dateCount'] = $courseObj->getStudyDateCount();
                $result['star'] = $courseObj->getStar();
                $result['nextCourseId'] = $nextCourseInfo['nextCourseId'];
                $result['nextLevel'] = $nextCourseInfo['nextLevel'];
                $result['isBuy'] = $nextCourseInfo['isBuy'];
            }
        }
        $lessonObj->setLastStudyTime($this->nowTimestamp());
        $lessonObj->save();

        return $this->sendSuccessJson($result);
    }

    /*
     * @api get /study-api/lessons/{lessonId}/winner 获取课程擂主
     * @version 0.0.1
     * @group web
     *
     * @param path int lessonId 课时ID
     *
     * @response 200 正常结果 {
     *  "code":0,
     *  "msg":"",
     *  "data":{}
     * }
     */
    public function winner($lessonId)
    {
        $redisKeyWinner = 'lesson_winner_' . $lessonId;
        $userInfo = [];
        // 优先取redis缓存中的winner数据
        $winner = Redis::hgetall($redisKeyWinner);
        if ($winner) {
            $winner = UserLesson::ofUserLesson($winner['user_id'], $lessonId)->first();
        } else {
            // redis中没有，再从数据库中
            /** @var UserLesson $winner */
            $winner = UserLesson::getGameWinner($lessonId);
        }
        if ($winner) {
            $userInfo = UserInternal::getByUserId($winner->getUserId());
            $userInfo['gameTime'] = UserLesson::convertTime($winner->getPlayGameTime());
            $userInfo['wordCount'] = $winner->getKnowledgeCount();

            // 缓存winner信息到redis
            Redis::hmset($redisKeyWinner, [
                'user_id'        => $winner->getUserId(),
                'play_game_time' => $winner->getPlayGameTime(),
            ]);
        }
        return $this->sendSuccessJson($userInfo);
    }

    /*
     * @api post /study-api/lessons/{lessonId}/game 玩游戏的结果
     * @version 0.0.1
     * @group web
     *
     * @param path int lessonId 课时ID
     * @param body int time 花费时间，单位s
     *
     * @response 200 正常结果 {
     *  "code":0,
     *  "msg":"",
     *  "data":{}
     * }
     */
    private function playGame($userId, $lessonId, $gameTime)
    {
        $isWinner = false;
        if ($gameTime) {
            // 比较时间是否比当前winner用的时间少，如果是则更新redis信息
            $redisKeyWinner = 'lesson_winner_' . $lessonId;
            $winner = Redis::hgetall($redisKeyWinner);
            $result = [];
            if ($winner) {
                if ($winner['play_game_time'] > $gameTime) {
                    $isWinner = true;
                    $result = [
                        'user_id' => $userId,
                        'play_game_time' => $gameTime
                    ];
                } else {
                    if ($winner['user_id'] == $userId) {
                        $isWinner = true;
                    }
                }
            } else {
                // 从数据库中查询
                /** @var UserLesson $winner */
                $winner = UserLesson::getGameWinner($lessonId);
                if ($winner) {
                    if ($winner->getPlayGameTime() > $gameTime) {
                        $isWinner = true;
                        $result = [
                            'user_id' => $userId,
                            'play_game_time' => $gameTime
                        ];
                    } else {
                        if ($winner['user_id'] == $userId) {
                            $isWinner = true;
                        }
                        $result = [
                            'user_id' => $winner->getUserId(),
                            'play_game_time' => $winner->getPlayGameTime()
                        ];
                    }
                } else {
                    // 数据库中无winner记录，当前用户为第一个完成用户
                    $isWinner = true;
                    $result = [
                        'user_id' => $userId,
                        'play_game_time' => $gameTime
                    ];
                }
            }
            $result && Redis::hmset($redisKeyWinner, $result);
        }
        return $isWinner;
    }

    private function getUserLesson($lessonId)
    {
        /** @var UserLesson $lessonObj */
        $lessonObj = UserLesson::ofUserLesson($this->userId, $lessonId)->first();
        if (!$lessonObj) {
            throw new LessonNotFoundException();
        }
        return $lessonObj;
    }

    private function lessonStar(&$lessonObj)
    {
        $totalScore = $lessonObj->getDubScore() + $lessonObj->getGameScore() + $lessonObj->getEssenceScore();
        $score = ceil($totalScore / 3);
        $star = ceil($totalScore / 100);
        $lessonObj->setScore($score);
        $lessonObj->setStar($star);
    }
}
